package com.zlp.partice.jvm;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;

import static java.lang.invoke.MethodHandles.lookup;

/**
 * @author: zhulaiping
 * @create: 2021-12-08 09:24
 * todo：
 *  1. 动态类型语言的关键特征是它的类型检查的主体过程是在运行期而不是编译期进行的
 *      变量无类型而变量值才有类型
 *  2. 静态类型语言能够在编译期确定变量类型，最显著的好处是编译器可以提供全面严谨的 类型检查，这样与数据类型相关的潜在问题就能在编码时被及时发现，
 *     利于稳定性及让项目容易达到 更大的规模。而动态类型语言在运行期才确定类型，这可以为开发人员提供极大的灵活性，某些在静 态类型语言中要花大量臃肿
 *     代码来实现的功能，由动态类型语言去做可能会很清晰简洁，清晰简洁通 常也就意味着开发效率的提升。
 **/
public class DynamicLanguage {

    static class ClassA {
        public void println(String s) {
            System.out.println("ClassA 的println方法");
            System.out.println(s);
        }
    }

    public static void main(String[] args) throws Throwable {
        Object obj = System.currentTimeMillis() % 2 == 0 ? System.out : new ClassA();
        // 无论obj最终是哪个实现类，下面这句都能正确调用到println方法。
        getPrintlnMH(obj).invokeExact("icyfenix");
    }

    private static MethodHandle getPrintlnMH(Object reveiver) throws Throwable {
        // MethodType：代表“方法类型”，包含了方法的返回值（methodType()的第一个参数）和具体参数（methodType()第二个及以后的参数）。
        MethodType mt = MethodType.methodType(void.class, String.class);
        // lookup()方法来自于MethodHandles.lookup，这句的作用是在指定类中查找符合给定的方法名称、方法类型，并且符合调用权限的方法句柄。
        // 因为这里调用的是一个虚方法，按照Java语言的规则，方法第一个参数是隐式的，代表该方法的接收者，也即this指向的对象，这个参数以前是放
        // 在参数列表中进行传递，现在提供了bindTo()法来完成这件事情。
        return lookup().findVirtual(reveiver.getClass(), "println", mt).bindTo(reveiver);
    }

}
